package tazmania.entity;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;

import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;

import tazadum.engine.Entity;
import tazadum.engine.Interval;
import tazadum.engine.Signal;
import tazadum.engine.math.Vector;
import tazadum.engine.text.Text;

public class ShowTextEntity implements Entity {
	private static final float GRAVITY = 250;

	private final Signal signal;

	private int squareList;
	private final List<Spawn> spawns = new LinkedList<Spawn>();

	private final float[][] colors = { { 1, 1, 1 }, { 1, 1, 0 }, { 0, 0, 0 }, { 1, 0, 0 } };
	private final int rotationSignal;

	public ShowTextEntity(final Signal signal, final int rotationSignal) {
		this.signal = signal;
		this.rotationSignal = rotationSignal;
	}

	@Override
	public void precalc(final GLAutoDrawable drawable, final float screenRatio) {
		final GL2 gl = drawable.getGL().getGL2();

		squareList = gl.glGenLists(1);

		gl.glNewList(squareList, GL2.GL_COMPILE);
		gl.glBegin(GL2.GL_QUADS);
		{
			final float S = .5f;
			final float R = 1;
			gl.glVertex2f(-R * S, -S);
			gl.glVertex2f(R * S, -S);
			gl.glVertex2f(R * S, S);
			gl.glVertex2f(-R * S, S);
		}
		gl.glEnd();
		gl.glEndList();
	}

	public void addText(final String text, final float ypos, final Interval interval) {
		spawns.add(new Spawn(text, ypos, interval.from(), interval.to()));
	}

	@Override
	public void update(final GLAutoDrawable drawable, final float globalTime, final float localTime, final float dt, final Interval interval) {
		final GL2 gl = drawable.getGL().getGL2();

		gl.glMatrixMode(GL2.GL_PROJECTION);
		gl.glPushMatrix();
		gl.glLoadIdentity();
		gl.glOrtho(0, 640, 480, 0, 0.1f, 1000);
		gl.glMatrixMode(GL2.GL_MODELVIEW);
		gl.glPushMatrix();
		gl.glLoadIdentity();
		gl.glTranslatef(0.0f, 0.0f, -4.0f);

		gl.glDisable(GL.GL_DEPTH_TEST);
		gl.glDisable(GL2.GL_LIGHTING);

		// final float R = signal.channel(Signal.CHANNEL_R);
		// final float G = signal.channel(Signal.CHANNEL_G);
		// final float B = signal.channel(Signal.CHANNEL_B);

		final float spawnTime = 0.025f;
		final float rotation = signal.channel(rotationSignal);

		final float time = localTime;

		final ListIterator<Spawn> iterator = spawns.listIterator();
		while (iterator.hasNext()) {
			final Spawn spawn = iterator.next();
			if (time > spawn.stop) {
				// stop animation
				if (spawn.stopped == 0) {
					for (final Vector p : spawn.positions) {
						p.x = 0;
						p.y = p.y * .5f + ((float) Math.random()) * GRAVITY;
						p.z = 0;
					}
					spawn.stopped = 1;
				}

				if (spawn.stopped == 1) {
					boolean outside = false;
					for (final Vector pos : spawn.letters) {
						outside |= pos.y < -2f;
					}
					if (outside) {
						iterator.remove();
						continue;
					}
				}

				int n = 0;
				final Iterator<Vector> original = spawn.letters.iterator();
				final Iterator<Vector> velocities = spawn.positions.iterator();
				while (original.hasNext()) {
					final Vector pos = original.next();
					final Vector velocity = velocities.next();

					pos.x += velocity.x * dt;
					pos.y += velocity.y * dt;
					pos.z += velocity.z * dt;

					velocity.y += dt * GRAVITY;

					gl.glPushMatrix();

					gl.glTranslatef(pos.x, pos.y, 0);
					final float scale = spawn.scale + ((n & 1) - .5f) * rotation * 10;
					gl.glScalef(scale, scale, 1);
					gl.glRotatef((time - spawn.stop + n) * 200f, 1, 1, 1);

					gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2.GL_FILL);
					final float[] c = colors[n % colors.length];
					gl.glColor4f(c[0], c[1], c[2], 1);
					gl.glCallList(squareList);

					gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2.GL_LINE);
					gl.glColor4f(0, 0, 0, 1);
					gl.glCallList(squareList);

					gl.glPopMatrix();
					n++;
				}
			} else if (time > spawn.start) {
				// start animation
				while (time >= spawn.nextActive) {
					spawn.actives++;
					spawn.nextActive += spawnTime;
				}

				int n = 0;
				final Iterator<Vector> original = spawn.letters.iterator();
				final Iterator<Vector> positions = spawn.positions.iterator();

				final float[] alpha = spawn.alpha;
				while (original.hasNext() && n < spawn.actives) {
					gl.glPushMatrix();

					final Vector dest = original.next();
					final Vector now = positions.next();

					alpha[n] += dt;
					if (alpha[n] >= 1) {
						alpha[n] = 1;
						gl.glTranslatef(dest.x, dest.y, 0);
					} else {
						final float x = (dest.x - now.x) * alpha[n] + now.x;
						final float y = (dest.y - now.y) * alpha[n] + now.y;
						gl.glTranslatef(x, y, 0);
					}
					final float scale = spawn.scale + ((n & 1) - .5f) * rotation * 10;
					gl.glScalef(scale, scale, 1);
					gl.glRotatef((1 - alpha[n]) * 600f, 1, 1, 1);

					gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2.GL_FILL);
					final float[] c = colors[n % colors.length];
					gl.glColor4f(c[0], c[1], c[2], 1);
					gl.glCallList(squareList);

					gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2.GL_LINE);
					gl.glColor4f(0, 0, 0, 1);
					gl.glCallList(squareList);

					gl.glPopMatrix();
					n++;
				}
			}
		}

		// gl.glMatrixMode(GL2.GL_MODELVIEW);
		gl.glPopMatrix();
		gl.glMatrixMode(GL2.GL_PROJECTION);
		gl.glPopMatrix();
	}

	@Override
	public void destroy(final GLAutoDrawable drawable) {
	}

	private static class Spawn {
		public List<Vector> letters;
		public List<Vector> positions;
		public float[] alpha;

		public float start;
		public float stop;
		public int actives = 0;
		public float nextActive = 0;
		public float scale;
		public int stopped = 0;

		public Spawn(final String text, final float ypos, final float start, final float stop) {
			this.stop = stop;
			this.scale = 600 / (float) (Text.SIZE * text.length());
			this.letters = Text.toVectors(text, scale, scale, 20, ypos);
			this.positions = new LinkedList<Vector>();
			this.start = start;
			this.alpha = new float[letters.size()];
			this.nextActive = start;

			for (final Vector quad : letters) {
				float x = quad.x;
				float y = quad.y;

				final boolean A = Math.random() >= 0.5;
				final boolean B = Math.random() >= 0.5;

				if (A) {
					x = B ? -20 : 700;
					y += (Math.random() - .5f) * 200;
				} else {
					y = B ? -20 : 520;
					x += (Math.random() - .5f) * 200;
				}
				positions.add(new Vector(x, y, 0));
			}
		}
	}
}
